package DumpParse;

use File::stat;
use Endian;

require Exporter;

our @ISA = qw(Exporter);

# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.

# This allows declaration	use IssDump ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw(
    Dump_Iu32AtPpc
    Dump_Iu16AtPpc
    Dump_Iu8AtPpc
    Dump_Load
) ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw(
	
);

my $Grawdata = "";
my $Gn_scatter = 0;
my @Gaddress_list = ();
my @Glength_list = ();

sub Dump_Load {
  my $path = shift;
  my $fh;

  if( ! open ($fh, "< :raw", $path) ) {
    warn "$!\n";
    return -1;
  }
  my $sb = stat($fh);
  sysread($fh, $Grawdata, $sb->size);

  if( Dump_StringAtOffset(0, 16) ne "ISS_ADAPTER_DUMP" ) {
    return -2;
  }

  $Gn_scatter = Dump_Iu32AtOffset(44, Endian::Little);
  print "$Gn_scatter\n";
  for(my $i = 0; $i < $Gn_scatter; $i++) {
    push @Gaddress_list, Dump_Iu32AtOffset(48+$i*8, Endian::Little);
    push @Glength_list, Dump_Iu32AtOffset(48+$i*8+4, Endian::Little);
  }

  return 0;
}

sub Dump_Iu32AtOffset {
  my $offset = shift;
  my $endian = shift;
  return undef if( !defined($offset) || $offset > length($Grawdata) - 4 );
  $ret = unpack("L", substr($Grawdata, $offset, 4));
  $ret = Endian::swap_u32($ret) if( $endian != Endian::get_endian() );
  return $ret;
}

sub Dump_Iu16AtOffset {
  my $offset = shift;
  my $endian = shift;
  return undef if( !defined($offset) || $offset > length($Grawdata) - 2 );
  $ret = unpack("S", substr($Grawdata, $offset, 2));
  $ret = Endian::swap_u16($ret) if( $endian != Endian::get_endian() );
  return $ret;
}

sub Dump_Iu8AtOffset {
  my $offset = shift;
  return undef if( !defined($offset) || $offset > length($Grawdata) - 1 );
  $ret = unpack("C", substr($Grawdata, $offset, 1));
  return $ret;
}

sub Dump_StringAtOffset {
  my $offset = shift;
  my $length = shift;
  return undef if( !defined($offset) || $offset > length($Grawdata) - $length );
  return undef if( !defined($length) || $length <= 0 );
  return unpack("A$length", substr($Grawdata, $offset, $length));
}

sub Dump_OffsetToPpc {
  my $offset = shift;
  return undef if( $offset < 512 );
  
  my $ppc = 0;
  my $i = 0;
  $offset -= 512;
  for( $i = 0; $i < $Gn_scatter; $i++ ) {
    if( $offset < $Glength_list[$i] ) {
      $ppc = $Gaddress_list[$i] + $offset;
      last;
    }
    $offset -= $Glength_list[$i];
  }
  return undef if( $i >= $Gn_scatter );
  return $ppc;
}

sub Dump_PpcToOffset {
  my $ppc = shift;
  my $offset = 512;
  my $i = 0;

  for( $i = 0; $i < $Gn_scatter; $i++ ) {
    if( $ppc >= $Gaddress_list[$i] &&
        $ppc < $Gaddress_list[$i] + $Glength_list[$i] ) {
      $offset += $ppc - $Gaddress_list[$i];
      last;
    }
    $offset += $Glength_list[$i];
  }
  if( $i >= $Gn_scatter ) {
    return undef;
  }
  return $offset;
}

sub Dump_PpcEndian {
  my $ppc = shift;
  return Endian::Little if( $ppc >= 0xf0000000 );
  return Endian::Little if( $ppc >= 0x800d0000 && $ppc <= 0x800d3fff );
  return Endian::Big;
}

sub Dump_Iu32AtPpc {
  my $ppc = shift;
  my $offset = Dump_PpcToOffset($ppc);
  return Dump_Iu32AtOffset($offset, Dump_PpcEndian($ppc));
}

sub Dump_Iu16AtPpc {
  my $ppc = shift;
  my $offset = Dump_PpcToOffset($ppc);
  return Dump_Iu16AtOffset($offset, Dump_PpcEndian($ppc));
}

sub Dump_Iu8AtPpc {
  my $ppc = shift;
  my $offset = Dump_PpcToOffset($ppc);
  return Dump_Iu8AtOffset($offset);
}

sub Dump_StringAtPpc {
  my $ppc = shift;
  my $length = shift;
  my $offset = Dump_PpcToOffset($ppc);
  return Dump_StringAtOffset($offset, $length);
}

1;
